#ifndef I2C_H
#define I2C_H

#ifdef __cplusplus
extern "C" {
#endif

//*****************************************************************************
//
//! \addtogroup i2c_api I2C
//! @{
//
//*****************************************************************************
#include <stdbool.h>
#include <stdint.h>

#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "cpu.h"
#include "debug.h"

#include "f28004x_device.h"

//*****************************************************************************
//
// I2C Controller commands.
//
//*****************************************************************************
typedef enum
{
    I2C_STANDARD_MODE = 0x01U,
    I2C_FAST_MODE     = 0x02U,
} I2C_SpeedMode;

typedef enum
{
    I2C_CONTROLLER_MASTER = 0x01U,
    I2C_CONTROLLER_SLAVE  = 0x02U,
} I2C_ControllerType;

//*****************************************************************************
//
// I2C interrupts for use with the intFlags parameter of I2C_clearInterruptStatus() and to be
// returned by I2C_getInterruptStatus().
//
//*****************************************************************************
#define I2C_INT_NONE           0x0000U
#define I2C_INT_RX_UNDER       0x0001U //!< Receive buffer under interrupt status
#define I2C_INT_RX_OVER        0x0002U //!< Receive buffer over  interrupt status
#define I2C_INT_RX_FULL        0x0004U //!< Receive buffer full  interrupt status
#define I2C_INT_TX_OVER        0x0008U //!< Transmit buffer over interrupt status
#define I2C_INT_TX_EMPTY       0x0010U //!< Transmit buffer empty interrupt status
#define I2C_INT_RD_REQ         0x0020U //!< Read request interrupt status
#define I2C_INT_TX_ABORT       0x0040U //!< Transmit abort interrupt status
#define I2C_INT_RX_DONE        0x0080U //!< i2c slave, the master does not acknowledge a transmitted byte
#define I2C_INT_ACTIVITY       0x0100U //!< This bit captures the active state of the I2C interface
#define I2C_INT_STOP_CONDITION 0x0200U //!< Whether a STOP condition has occurred.
#define I2C_INT_START_DET      0x0400U //!< Whether a START or RESTART condition has occurred
#define I2C_INT_GEN_CALL       0x0800U //!< Set only when a General Call address is received
#define I2C_INT_ALL            0x0FFFU

//*****************************************************************************
//
// Flags for use as the stsFlags parameter of I2C_getStatus().
//
//*****************************************************************************
#define I2C_STS_ACTIVITY               0x0001 //!< I2C activity status
#define I2C_STS_TX_FIFO_NOT_FULL       0x0002 //!< I2C Transmit FIFO not full status
#define I2C_STS_TX_FIFO_COMPLETE_EMPTY 0x0004 //!< I2C Transmit FIFO completely empty status
#define I2C_STS_RX_FIFO_NOT_FULL       0x0008 //!< I2C Receive FIFO not full status
#define I2C_STS_RX_FIFO_FULLY_FULL     0x0010 //!< I2C Receive FIFO fully full status
#define I2C_STS_MASTER_FSM_ACTIVE      0x0020 //!< I2C Master FSM active
#define I2C_STS_SLAVE_FSM_ACTIVE       0x0040 //!< I2C Slave  FSM active

//*****************************************************************************
//
// Flags for use as the stsFlags parameter of I2C_getAbortStatus().
//
//*****************************************************************************
#define I2C_ABRT_SRC_7B_ADDR_NOACK   0x0001
#define I2C_ABRT_SRC_10ADDR1_NOACK   0x0002
#define I2C_ABRT_SRC_10ADDR2_NOACK   0x0004
#define I2C_ABRT_SRC_TXDATA_NOACK    0x0008
#define I2C_ABRT_SRC_GCALL_NOACK     0x0010
#define I2C_ABRT_SRC_GCALL_READ      0x0020
#define I2C_ABRT_SRC_SBYTE_ACKDET    0x0080
#define I2C_ABRT_SRC_SBYTE_NORSTRT   0x0200
#define I2C_ABRT_SRC_10B_RD_NORSTRT  0x0400
#define I2C_ABRT_SRC_MASTER_DIS      0x0800
#define I2C_ABRT_SRC_LOST            0x1000
#define I2C_ABRT_SRC_SLVFLUSH_TXFIFO 0x2000
#define I2C_ABRT_SRC_SLV_ARBLOST     0x4000
#define I2C_ABRT_SRC_SLVRD_INTX      0x8000
#define I2C_ABRT_SRC_USER_ABRT       0x10000

//*****************************************************************************
//
//! Values that can be passed to I2C_setFIFOInterruptLevel() as the \e txLevel
//! parameter, returned by I2C_getFIFOInterruptLevel() in the \e txLevel
//! parameter, and returned by I2C_getTxFIFOStatus().
//
//*****************************************************************************
typedef enum
{
    I2C_FIFO_TX0 = 0x0000U, //!< Transmit FIFO 1/8 full
    I2C_FIFO_TX1 = 0x0001U, //!< Transmit FIFO 2/8 full
    I2C_FIFO_TX2 = 0x0002U, //!< Transmit FIFO 3/8 full
    I2C_FIFO_TX3 = 0x0003U, //!< Transmit FIFO 4/8 full
    I2C_FIFO_TX4 = 0x0004U, //!< Transmit FIFO 5/8 full
    I2C_FIFO_TX5 = 0x0005U, //!< Transmit FIFO 6/8 full
    I2C_FIFO_TX6 = 0x0006U, //!< Transmit FIFO 7/8 full
    I2C_FIFO_TX7 = 0x0007U, //!< Transmit FIFO 8/8 full
} I2C_TxFIFOLevel;

//*****************************************************************************
//
//! Values that can be passed to I2C_setFIFOInterruptLevel() as the \e rxLevel
//! parameter, returned by I2C_getFIFOInterruptLevel() in the \e rxLevel
//! parameter, and returned by I2C_getRxFIFOStatus().
//
//*****************************************************************************
typedef enum
{
    I2C_FIFO_RX1 = 0x0000U, //!< Receive FIFO 1/8 full
    I2C_FIFO_RX2 = 0x0001U, //!< Receive FIFO 2/8 full
    I2C_FIFO_RX3 = 0x0002U, //!< Receive FIFO 3/8 full
    I2C_FIFO_RX4 = 0x0003U, //!< Receive FIFO 4/8 full
    I2C_FIFO_RX5 = 0x0004U, //!< Receive FIFO 5/8 full
    I2C_FIFO_RX6 = 0x0005U, //!< Receive FIFO 6/8 full
    I2C_FIFO_RX7 = 0x0006U, //!< Receive FIFO 7/8 full
    I2C_FIFO_RX8 = 0x0007U, //!< Receive FIFO 8/8 full
} I2C_RxFIFOLevel;

//*****************************************************************************
//
//! Values that can be passed to I2C_setAddressMode() as the \e mode parameter.
//
//*****************************************************************************
typedef enum
{
    I2C_ADDR_MODE_7BITS  = 0x00U, //!< 7-bit address
    I2C_ADDR_MODE_10BITS = 0x01U  //!< 10-bit address
} I2C_AddressMode;

//*****************************************************************************
//
//! \internal
//! Checks an I2C base address.
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function determines if a I2C module base address is valid.
//!
//! \return Returns \b true if the base address is valid and \b false
//! otherwise.
//
//*****************************************************************************
#ifdef DEBUG
static inline bool I2C_isBaseValid(uint32_t base)
{
    return (base == I2CA_BASE);
}
#endif

//*****************************************************************************
//
//! Enables the I2C module.
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function enables operation of the I2C module.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_enableModule(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_ENABLE) |= I2C_EN_ENABLE;
}

//*****************************************************************************
//
//! Disables the I2C module.
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function disables operation of the I2C module.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_disableModule(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_ENABLE) &= ~(I2C_EN_ENABLE);
}

static inline void I2C_setRxFIFOInterruptLevel(uint32_t base, I2C_RxFIFOLevel rxLevel)
{
    HWREG(base + I2C_O_RX_TL) = (HWREG(base + I2C_O_RX_TL) & (~I2C_RX_TL)) | rxLevel;
}

static inline void I2C_setTxFIFOInterruptLevel(uint32_t base, I2C_TxFIFOLevel txLevel)
{
    HWREG(base + I2C_O_TX_TL) = (HWREG(base + I2C_O_TX_TL) & (~I2C_TX_TL)) | txLevel;
}

//*****************************************************************************
//
//! Sets the FIFO level at which interrupts are generated.
//!
//! \param base is the base address of the I2C instance used.
//! \param txLevel is the transmit FIFO interrupt level, specified as
//! \b I2C_FIFO_TX0, \b I2C_FIFO_TX1, \b I2C_FIFO_TX2, . . . or
//! \b I2C_FIFO_TX8.
//! \param rxLevel is the receive FIFO interrupt level, specified as
//! \b I2C_FIFO_RX1, \b I2C_FIFO_RX2, . . . or
//! \b I2C_FIFO_RX8.
//!
//! This function sets the FIFO level at which transmit and receive interrupts
//! are generated.  The transmit FIFO interrupt flag will be set when the FIFO
//! reaches a value less than or equal to \e txLevel.  The receive FIFO
//! flag will be set when the FIFO reaches a value greater than or equal to
//! \e rxLevel.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_setFIFOInterruptLevel(
    uint32_t base, I2C_TxFIFOLevel txLevel, I2C_RxFIFOLevel rxLevel)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Set the FIFO interrupt levels.
    //
    HWREG(base + I2C_O_RX_TL) = (HWREG(base + I2C_O_RX_TL) & (~I2C_RX_TL)) | rxLevel;
    HWREG(base + I2C_O_TX_TL) = (HWREG(base + I2C_O_TX_TL) & (~I2C_TX_TL)) | txLevel;
}

//*****************************************************************************
//
//! Gets the FIFO level at which interrupts are generated.
//!
//! \param base is the base address of the I2C instance used.
//! \param txLevel is a pointer to storage for the transmit FIFO level,
//! returned as one of \b I2C_FIFO_TX0, \b I2C_FIFO_TX1,
//! \b I2C_FIFO_TX2, . . . or \b I2C_FIFO_TX8.
//! \param rxLevel is a pointer to storage for the receive FIFO level,
//! returned as one of \b I2C_FIFO_RX1,
//! \b I2C_FIFO_RX2, . . . or \b I2C_FIFO_RX8.
//!
//! This function gets the FIFO level at which transmit and receive interrupts
//! are generated.  The transmit FIFO interrupt flag will be set when the FIFO
//! reaches a value less than or equal to \e txLevel.  The receive FIFO
//! flag will be set when the FIFO reaches a value greater than or equal to
//! \e rxLevel.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_getFIFOInterruptLevel(
    uint32_t base, I2C_TxFIFOLevel *txLevel, I2C_RxFIFOLevel *rxLevel)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Extract the transmit and receive FIFO levels.
    //
    *txLevel = (I2C_TxFIFOLevel)HWREG(base + I2C_O_TX_TL);
    *rxLevel = (I2C_RxFIFOLevel)HWREG(base + I2C_O_RX_TL);
}

//*****************************************************************************
//
//! Get the transmit FIFO status
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function gets the current number of words in the transmit FIFO.
//!
//! \return Returns the current number of words in the transmit FIFO specified
//! as one of the following:
//! \b I2C_FIFO_TX0, \b I2C_FIFO_TX1, \b I2C_FIFO_TX2, \b I2C_FIFO_TX3,
//! ..., or \b I2C_FIFO_TX16
//
//*****************************************************************************

static inline I2C_TxFIFOLevel I2C_getTxFIFOStatus(uint32_t base)
{
    uint32_t level;
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Get the current FIFO status
    //
    level = (HWREG(base + I2C_O_TXFLR) & I2C_TXFLR);

    return ((I2C_TxFIFOLevel)level);
}

//*****************************************************************************
//
//! Get the receive FIFO status
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function gets the current number of words in the receive FIFO.
//!
//! \return Returns the current number of words in the receive FIFO specified
//! as one of the following:
//! \b I2C_FIFO_RX1, \b I2C_FIFO_RX2, \b I2C_FIFO_RX3,
//! ..., or \b I2C_FIFO_RX8
//
//*****************************************************************************
static inline I2C_RxFIFOLevel I2C_getRxFIFOStatus(uint32_t base)
{
    uint32_t level;

    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Get the current FIFO status
    //
    level = (HWREG(base + I2C_O_RXFLR) & I2C_RXFLR);

    return ((I2C_RxFIFOLevel)level);
}

//*****************************************************************************
//
//! Sets the address that the I2C Controller places on the bus.
//!
//! \param base is the base address of the I2C instance used.
//! \param targetAddr 7-bit or 10-bit target address
//!
//! This function configures the address that the I2C Controller places on the bus
//! when initiating a transaction.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_setTargetAddress(uint32_t base, uint16_t targetAddr)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));
    ASSERT(targetAddr <= I2C_TAR_TAR_M);

    HWREG(base + I2C_O_TAR) = (HWREG(base + I2C_O_TAR) & (~I2C_TAR_TAR_M)) | targetAddr;
}

//*****************************************************************************
//
//! Sets the own address for this I2C module.
//!
//! \param base is the base address of the I2C Target module.
//! \param Addr is the 7-bit or 10-bit address
//!
//! This function writes the specified address.
//!
//! The parameter \e Addr is the value that is compared against the
//! target address sent by an I2C controller.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_setOwnAddress(uint32_t base, uint16_t Addr)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));
    ASSERT(Addr <= I2C_OAR_OAR_M);

    HWREG(base + I2C_O_OAR) = (HWREG(base + I2C_O_OAR) & (~I2C_OAR_OAR_M)) | Addr;
}

//*****************************************************************************
//
//! Indicates whether or not the I2C bus is busy.
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function returns an indication of whether or not the I2C bus is busy.
//! This function can be used in a multi-controller environment to determine if the
//! bus is free for another data transfer.
//!
//! \return Returns \b true if the I2C bus is busy; otherwise, returns
//! \b false.
//
//*****************************************************************************
static inline bool I2C_isBusBusy(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    return (bool)(HWREG(base + I2C_O_STR) & I2C_STR_ACTIVITY_M);
}

//*****************************************************************************
//
//! Gets the current I2C module status.
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function returns the status for the I2C module.
//!
//! \return The current module status, enumerated as a bit field of
//! - \b I2C_STS_ACTIVITY                 - I2C activity status
//! - \b I2C_STS_TX_FIFO_NOT_FULL         - I2C Transmit FIFO not full status
//! - \b I2C_STS_TX_FIFO_COMPLETE_EMPTY   - I2C Transmit FIFO completely empty status
//! - \b I2C_STS_RX_FIFO_NOT_FULL         - I2C Receive FIFO not full status
//! - \b I2C_STS_RX_FIFO_FULLY_FULL       - I2C Receive FIFO fully full status
//! - \b I2C_STS_MASTER_FSM_ACTIVE        - I2C Master FSM active
//! - \b I2C_STS_SLAVE_FSM_ACTIVE         - I2C Slave  FSM active
//
//*****************************************************************************
static inline uint32_t I2C_getStatus(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Return contents of the status register
    //
    return (HWREG(base + I2C_O_STR));
}

//*****************************************************************************
//
//! Controls the state of the I2C module.
//!
//! \param base is the base address of the I2C instance used.
//! \param config is the command to be issued to the I2C  module.
//!
//! This function is used to control the state of the controller and target send and
//! receive operations. The \e config is a logical OR of the following options.
//!
//! Must One of the following options:
//! - \b I2C_CONTROLLER_MASTER  - Controller-transmitter mode
//! - \b I2C_CONTROLLER_SLAVE   - Controller-receiver    mode
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_setConfig(uint32_t base, uint32_t config)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Write the selected options to the mode register.
    //
    if (I2C_CONTROLLER_MASTER == config)
    {
        HWREG(base + I2C_O_CON)
            |= (I2C_CON_MASTER_MODE | I2C_CON_SLAVE_DISABLE); // bit6 = 1, bit0 = 1: Master Device
    }
    else if (I2C_CONTROLLER_SLAVE == config)
    {
        HWREG(base + I2C_O_CON)
            &= (~(I2C_CON_MASTER_MODE | I2C_CON_SLAVE_DISABLE)); // bit6 = 0, bit0 = 0: Slave Device
    }
}

//*****************************************************************************
//
//! Controls the speed of the I2C module.
//!
//! - \b I2C_STANDARD_MODE
//! - \b I2C_FAST_MODE
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_setSpeedMode(uint32_t base, I2C_SpeedMode mode)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Write speed mode
    //
    HWREG(base + I2C_O_CON)
        = (HWREG(base + I2C_O_CON) & (~I2C_CON_SPEED)) | (uint32_t)(mode << I2C_CON_SPEED_S);
}

//*****************************************************************************
//
//! Issues an I2C START condition.
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function causes the I2C module to generate a start condition. This
//! function is only valid when the I2C module specified by the \b base
//! parameter is a controller.
//!
//! This bit controls whether a read or a write is performed. This bit does not
//! control the direction when the DW_apb_i2c acts as a slave. It controls only
//! the direction when it acts as a master.
//!
//! \b 0 = Write - I2C_sendStartConditionWrite()
//! \b 1 = Read  - I2C_sendStartConditionRead()
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_sendStartConditionWrite(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_DATA_CMD) &= (~I2C_DATA_CMD_CMD);
}

static inline void I2C_sendStartConditionRead(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_DATA_CMD) |= I2C_DATA_CMD_CMD;
}

//*****************************************************************************
//
//! Enable restart signal.
//!
//! \param base is the base address of the I2C instance used.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_enableRestart(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_CON) |= I2C_CON_RESTART_EN;
}

//*****************************************************************************
//
//! Disable restart signal.
//!
//! \param base is the base address of the I2C instance used.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_disableRestart(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_CON) &= (~I2C_CON_RESTART_EN);
}

//*****************************************************************************
//
//! Issues a no-acknowledge (NACK) bit.
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function causes the I2C module to generate a NACK bit. This is only
//! applicable when the I2C module is acting as a receiver.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_sendNACK(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_SLV_DATA_NACK_ONLY) |= I2C_SLV_DATA_NACK_ONLY_NACK;
}

//*****************************************************************************
//
//! Receives a byte that has been sent to the I2C.
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function reads a byte of data from the I2C Data Receive Register.
//!
//! \return Returns the byte received from by the I2C cast as an uint8_t.
//
//*****************************************************************************
static inline uint8_t I2C_getData(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Return the contents of the receive register.
    //
    return (uint8_t)(HWREG(base + I2C_O_DATA_CMD) & I2C_DATA_CMD_DATA);
}

//*****************************************************************************
//
//! Transmits a byte from the I2C.
//!
//! \param base is the base address of the I2C instance used.
//! \param data is the data to be transmitted from the I2C Controller.
//!
//! This function places the supplied data into I2C Data Transmit Register.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_putData(uint32_t base, uint8_t data)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Place the data into the transmit register.
    //
    HWREG(base + I2C_O_DATA_CMD) = (HWREG(base + I2C_O_DATA_CMD) & (~I2C_DATA_CMD_DATA)) | data;
}

//*****************************************************************************
//
//! Sets the addressing mode to either 7-bit or 10-bit.
//!
//! \param base is the base address of the I2C instance used.
//! \param mode is the address mode, 7-bit or 10-bit.
//!
//! This function configures the I2C module for either a 7-bit address
//! (default) or a 10-bit address. The \e mode parameter configures the address
//! length to 10 bits when its value is \b I2C_ADDR_MODE_10BITS and 7 bits when
//! \b I2C_ADDR_MODE_7BITS.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_setAddressMode(uint32_t base, I2C_AddressMode mode)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    //
    // Write the appropriate value to the address expansion bit.
    //
    if (I2C_ADDR_MODE_7BITS == mode)
    {
        HWREG(base + I2C_O_CON) &= (~(I2C_CON_10BITADDR_SLAVE | I2C_CON_10BITADDR_MASTER));
    }
    else if (I2C_ADDR_MODE_10BITS == mode)
    {
        HWREG(base + I2C_O_CON) |= (I2C_CON_10BITADDR_SLAVE | I2C_CON_10BITADDR_MASTER);
    }
}

//*****************************************************************************
//
//! In slave mode: Whether to generate a STOP interrupt.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_enableStopDetIfAddress(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_CON) |= I2C_CON_STOP_DET_IFADDRESSED;
}

static inline void I2C_disableStopDetIfAddress(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_CON) &= ~I2C_CON_STOP_DET_IFADDRESSED;
}

//*****************************************************************************
//
//! Set level period of SCL clock
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_setSCLLowCount(uint32_t base, uint16_t count)
{
    uint8_t speed_mode = (uint8_t)((HWREG(base + I2C_O_CON) & I2C_CON_SPEED) >> I2C_CON_SPEED_S);

    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    if (I2C_STANDARD_MODE == speed_mode)
    {
        HWREG(base + I2C_O_SS_SCL_LCNT)
            = (HWREG(base + I2C_O_SS_SCL_LCNT) & (~I2C_SS_SCL_LCNT)) | count;
    }
    else if (I2C_FAST_MODE == speed_mode)
    {
        HWREG(base + I2C_O_FS_SCL_LCNT)
            = (HWREG(base + I2C_O_FS_SCL_LCNT) & (~I2C_FS_SCL_LCNT)) | count;
    }
}

static inline void I2C_setSCLHighCount(uint32_t base, uint16_t count)
{
    uint8_t speed_mode = (uint8_t)((HWREG(base + I2C_O_CON) & I2C_CON_SPEED) >> I2C_CON_SPEED_S);

    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    if (I2C_STANDARD_MODE == speed_mode)
    {
        HWREG(base + I2C_O_SS_SCL_HCNT)
            = (HWREG(base + I2C_O_SS_SCL_HCNT) & (~I2C_SS_SCL_HCNT)) | count;
    }
    else if (I2C_FAST_MODE == speed_mode)
    {
        HWREG(base + I2C_O_FS_SCL_HCNT)
            = (HWREG(base + I2C_O_FS_SCL_HCNT) & (~I2C_FS_SCL_HCNT)) | count;
    }
}

//*****************************************************************************
//
//! Set the required in ic_clk cycles SDA hold time.
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_setSDAHold(uint32_t base, uint16_t rxHold, uint16_t txHold)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_SDA_HOLD) = (HWREG(base + I2C_O_SDA_HOLD) & (~I2C_SDA_HOLD_TX)) | txHold;
    HWREG(base + I2C_O_SDA_HOLD)
        = (HWREG(base + I2C_O_SDA_HOLD) & (~I2C_SDA_HOLD_RX)) | (rxHold << I2C_SDA_HOLD_RX_S);
}

//*****************************************************************************
//
//! Set by spikes Suppression logic filters out the longest spikes on the SCL or SDA line Duration,
//! measured in ic_clk cycles
//!
//! \return None.
//
//*****************************************************************************
static inline void I2C_setSpikeSuppression(uint32_t base, uint8_t count)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    HWREG(base + I2C_O_FS_SPKLEN) = (HWREG(base + I2C_O_FS_SPKLEN) & (~I2C_FS_SPKLEN)) | count;
}

//*****************************************************************************
//
//! I2C transfer abort source register.
//!
//! The \e Flags parameter is the logical OR of any of the following:
//! \b I2C_ABRT_SRC_7B_ADDR_NOACK   -
//! \b I2C_ABRT_SRC_10ADDR1_NOACK   -
//! \b I2C_ABRT_SRC_10ADDR2_NOACK   -
//! \b I2C_ABRT_SRC_TXDATA_NOACK    -
//! \b I2C_ABRT_SRC_GCALL_NOACK     -
//! \b I2C_ABRT_SRC_GCALL_READ      -
//! \b I2C_ABRT_SRC_SBYTE_ACKDET    -
//! \b I2C_ABRT_SRC_SBYTE_NORSTRT   -
//! \b I2C_ABRT_SRC_10B_RD_NORSTRT  -
//! \b I2C_ABRT_SRC_MASTER_DIS      -
//! \b I2C_ABRT_SRC_LOST            -
//! \b I2C_ABRT_SRC_SLVFLUSH_TXFIFO -
//! \b I2C_ABRT_SRC_SLV_ARBLOST     -
//! \b I2C_ABRT_SRC_SLVRD_INTX      -
//! \b I2C_ABRT_SRC_USER_ABRT       -
//! \b ...
//
//*****************************************************************************
static inline uint32_t I2C_getAbortStatus(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(I2C_isBaseValid(base));

    return HWREG(base + I2C_O_TX_ABRT_SOURCE);
}

//*****************************************************************************
//
//! Initializes the I2C Controller.
//!
//! \param base is the base address of the I2C instance used.
//!
//! \return None.
//
//*****************************************************************************
extern void I2C_initController(uint32_t base, uint32_t sysclkHz, uint32_t bitRate);

//*****************************************************************************
//
//! Enables I2C interrupt sources.
//!
//! \param base is the base address of the I2C instance used.
//! \param intFlags is the bit mask of the interrupt sources to be enabled.
//!
//! This function enables the indicated I2C Controller interrupt sources.  Only the
//! sources that are enabled can be reflected to the processor interrupt.
//! Disabled sources have no effect on the processor.
//!
//! The \e intFlags parameter is the logical OR of any of the following:
//! \b I2C_INT_RX_UNDER        - Receive buffer under interrupt status
//! \b I2C_INT_RX_OVER         - Receive buffer over  interrupt status
//! \b I2C_INT_RX_FULL         - Receive buffer full  interrupt status
//! \b I2C_INT_TX_OVER         - Transmit buffer over interrupt status
//! \b I2C_INT_TX_EMPTY        - Transmit buffer empty interrupt status
//! \b I2C_INT_RD_REQ          - Read request interrupt status
//! \b I2C_INT_TX_ABORT        - Transmit abort interrupt status
//! \b I2C_INT_RX_DONE         - i2c slave, the master does not acknowledge a transmitted byte
//! \b I2C_INT_ACTIVITY        - This bit captures the active state of the I2C
//! \b I2C_INT_STOP_CONDITION  - Whether a STOP condition has occurred.
//! \b I2C_INT_START_DET       - Whether a START or RESTART condition has occurred
//! \b I2C_INT_GEN_CALL        - Set only when a General Call address is received
//! \b I2C_INT_ALL
//! \b ...
//!
//! \return None.
//
//*****************************************************************************
extern void I2C_enableInterrupt(uint32_t base, uint32_t intFlags);

//*****************************************************************************
//
//! Disables I2C interrupt sources.
//!
//! \param base is the base address of the I2C instance used.
//! \param intFlags is the bit mask of the interrupt sources to be disabled.
//!
//! This function disables the indicated I2C Target interrupt sources.  Only
//! the sources that are enabled can be reflected to the processor interrupt.
//! Disabled sources have no effect on the processor.
//!
//! The \e intFlags parameter has the same definition as the \e intFlags
//! parameter to I2C_enableInterrupt().
//! \b I2C_INT_RX_UNDER        - Receive buffer under interrupt status
//! \b I2C_INT_RX_OVER         - Receive buffer over  interrupt status
//! \b I2C_INT_RX_FULL         - Receive buffer full  interrupt status
//! \b I2C_INT_TX_OVER         - Transmit buffer over interrupt status
//! \b I2C_INT_TX_EMPTY        - Transmit buffer empty interrupt status
//! \b I2C_INT_RD_REQ          - Read request interrupt status
//! \b I2C_INT_TX_ABORT        - Transmit abort interrupt status
//! \b I2C_INT_RX_DONE         - i2c slave, the master does not acknowledge a transmitted byte
//! \b I2C_INT_ACTIVITY        - This bit captures the active state of the I2C
//! \b I2C_INT_STOP_CONDITION  - Whether a STOP condition has occurred.
//! \b I2C_INT_START_DET       - Whether a START or RESTART condition has occurred
//! \b I2C_INT_GEN_CALL        - Set only when a General Call address is received
//! \b I2C_INT_ALL
//! \b ...
//!
//! \return None.
//
//*****************************************************************************
extern void I2C_disableInterrupt(uint32_t base, uint32_t intFlags);

//*****************************************************************************
//
//! Gets the current I2C interrupt status.
//!
//! \param base is the base address of the I2C instance used.
//!
//! This function returns the interrupt status for the I2C module.
//!
//! \return The current interrupt status, enumerated as a bit field of
//! \b I2C_INT_RX_UNDER        - Receive buffer under interrupt status
//! \b I2C_INT_RX_OVER         - Receive buffer over  interrupt status
//! \b I2C_INT_RX_FULL         - Receive buffer full  interrupt status
//! \b I2C_INT_TX_OVER         - Transmit buffer over interrupt status
//! \b I2C_INT_TX_EMPTY        - Transmit buffer empty interrupt status
//! \b I2C_INT_RD_REQ          - Read request interrupt status
//! \b I2C_INT_TX_ABORT        - Transmit abort interrupt status
//! \b I2C_INT_RX_DONE         - i2c slave, the master does not acknowledge a transmitted byte
//! \b I2C_INT_ACTIVITY        - This bit captures the active state of the I2C
//! \b I2C_INT_STOP_CONDITION  - Whether a STOP condition has occurred.
//! \b I2C_INT_START_DET       - Whether a START or RESTART condition has occurred
//! \b I2C_INT_GEN_CALL        - Set only when a General Call address is received
//! \b I2C_INT_ALL
//! \b ...
//!
//! \note This function will only return the status flags associated with
//! interrupts. However, a flag may be set even if its corresponding interrupt
//! is disabled.
//
//*****************************************************************************
extern uint32_t I2C_getInterruptStatus(uint32_t base);

//*****************************************************************************
//
//! Clears I2C interrupt sources.
//!
//! \param base is the base address of the I2C instance used.
//! \param intFlags is a bit mask of the interrupt sources to be cleared.
//!
//! The specified I2C interrupt sources are cleared, so that they no longer
//! assert.  This function must be called in the interrupt handler to keep the
//! interrupt from being triggered again immediately upon exit.
//!
//! The \e intFlags parameter has the same definition as the \e intFlags
//! parameter to I2C_enableInterrupt().
//!
//! \b I2C_INT_RX_UNDER        - Receive buffer under interrupt status
//! \b I2C_INT_RX_OVER         - Receive buffer over  interrupt status
//! \b I2C_INT_RX_FULL         - Receive buffer full  interrupt status
//! \b I2C_INT_TX_OVER         - Transmit buffer over interrupt status
//! \b I2C_INT_TX_EMPTY        - Transmit buffer empty interrupt status
//! \b I2C_INT_RD_REQ          - Read request interrupt status
//! \b I2C_INT_TX_ABORT        - Transmit abort interrupt status
//! \b I2C_INT_RX_DONE         - i2c slave, the master does not acknowledge a transmitted byte
//! \b I2C_INT_ACTIVITY        - This bit captures the active state of the I2C
//! \b I2C_INT_STOP_CONDITION  - Whether a STOP condition has occurred.
//! \b I2C_INT_START_DET       - Whether a START or RESTART condition has occurred
//! \b I2C_INT_GEN_CALL        - Set only when a General Call address is received
//! \b I2C_INT_ALL
//! \b ...
//!
//! \note Also note that some of the status flags returned by
//! I2C_getInterruptStatus() cannot be cleared by this function. Some may only
//! be cleared by hardware or a reset of the I2C module.
//!
//! \return None.
//
//*****************************************************************************
extern void I2C_clearInterruptStatus(uint32_t base, uint32_t intFlags);

/********************************************************************************/
/* temp define of i2c lib */
#define     I2CFREQ_NORMAL      100000
#define     I2CFREQ_RAPID       1000000
#define     I2CFREQ_HIGHSPEED   3400000

enum i2cRole_e
{
    I2CROLE_SLAVE = 0,
    I2CROLE_MASTER,
};

enum i2cBitWide_e
{
    I2CBITWIDE_7 = 0,
    I2CBITWIDE_10,
};

enum i2cEnable_e
{
    I2CENABLE = 1,
    I2CDISABLE = 0,
};

enum i2cInterrupt_e
{
    I2CINTERRUPT_RXFULL = (uint32_t)(1 << 2),
    I2CINTERRUPT_TX_OVERFLOW = (uint32_t)(1 << 3),
    I2CINTERRUPT_TX_EMPTY = (uint32_t)(1 << 4),
    I2CINTERRUPT_RDREQ = (uint32_t)(1 << 5),
    I2CINTERRUPT_STOP = (uint32_t)(1 << 9),
};

void I2cInitial(void);
uint16_t GetI2cbRxData(uint8_t **data);
uint16_t GetI2caRxData(uint8_t **data);

void I2cPeripheralEnable(volatile struct I2C_REGS * address, enum i2cEnable_e enable);
//void I2cSetFrequency(volatile struct I2C_REGS * address, uint32_t frequency);
void I2cSetRole(volatile struct I2C_REGS * address, enum i2cRole_e role);
void I2cSetAddrBitWide(volatile struct I2C_REGS * address, enum i2cBitWide_e bitWide);
void I2cEnableRestart(volatile struct I2C_REGS * address, enum i2cEnable_e enable);
void I2CSetSelfId(volatile struct I2C_REGS * address, uint16_t id);
void I2CSetDestId(volatile struct I2C_REGS * address, uint16_t id);
void I2cInterruptInitial(volatile struct I2C_REGS * address);
void I2CEnableInterrupt(volatile struct I2C_REGS * address, enum i2cInterrupt_e interrupt, enum i2cEnable_e enable);
void I2cSendData(volatile struct I2C_REGS * address, uint8_t data);
void I2cReadCmd(volatile struct I2C_REGS * address);
uint8_t I2cReadData(volatile struct I2C_REGS * address);
void I2cClearIsrAllStatus(volatile struct I2C_REGS * address);
uint32_t I2cGetIsrStatus(volatile struct I2C_REGS * address);
/********************************************************************************/
void i2c_control_tx_empty_signal(volatile struct I2C_REGS * address,uint8_t signal);
void i2c_generate_stop_signal(volatile struct I2C_REGS * address);
void i2c_generate_restart_signal(volatile struct I2C_REGS * address);

#ifdef __cplusplus
}
#endif

#endif
